// 
// $Id$
//
// Implementation of USB_Device class
//
// <Nitin Karkhanis> <<nkarkhan@yahoo.com>>
// Created on: <31-Jan-2007 14:55:24 nitin_karkhanis>
//
// Copyright (C) <Nitin Karkhanis>.  All rights reserved.
//


#include <stdlib.h>
#include <stdio.h>
#include <iostream>
#include <FTDI_Device.hpp>

using namespace std;

/*! \file FTDI_Device.cpp
  Implementation of FTDI_Device class
*/

/*!
  \class FTDI_Device FTDI_Device.hpp
  \brief
  \author <a href = "mailto:<nkarkhan@yahoo.com>"><Nitin Karkhanis></a>
  \date Enter current date here

*/

/*!
  Default constructor
*/


FTDI_Device::FTDI_Device (long LocationID):
    pin_mode(0), write_val(0), found(false)
{
    FT_STATUS ftStatus;
    ftStatus     = FT_OpenEx((PVOID)LocationID, FT_OPEN_BY_LOCATION, &ft_handle);
    if (ftStatus != FT_OK) {
        // failure
//        cout << __FUNCTION__ << " Device open failed" << endl;
        return;
    }
    // success - device at location is open
    init_device();
}

FTDI_Device::FTDI_Device ():
    pin_mode(0), write_val(0), found(false)
{
    FT_STATUS ftStatus;
    DWORD     numDevs;

    //Get the number of devices currently connected

    ftStatus = FT_ListDevices(&numDevs, NULL, FT_LIST_NUMBER_ONLY);
    if ((ftStatus == FT_OK) || (0 == numDevs)) {
        // FT_ListDevices OK, number of devices connected is in numDevs
//        cout << __FUNCTION__ << " Devices discovered = " << numDevs << " ftstatus = " << ftStatus << endl;
        if (0 == numDevs) return;
    }
    //Get Location of devices connected
    long  LocationID[16]; // more than enough room!

    ftStatus = FT_ListDevices((PVOID)0,
                              LocationID,
                              FT_LIST_BY_INDEX|FT_OPEN_BY_LOCATION);
    if (ftStatus != FT_OK) {
        // failure
//        cout << __FUNCTION__ << " FT_ListDevices failed" << endl;
        return;
    }
    ftStatus     = FT_OpenEx((PVOID)LocationID[0], FT_OPEN_BY_LOCATION, &ft_handle);
    if (ftStatus != FT_OK) {
        // failure
//        cout << __FUNCTION__ << " Device open failed" << endl;
        return;
    }
    init_device();
}

FTDI_Device::~FTDI_Device ()
{
    FT_Close(ft_handle);
}

int
FTDI_Device::init_device(void)
{
    FT_STATUS ftStatus;

    // Reset device
    Sleep(100);
    ftStatus = FT_ResetDevice(ft_handle);
    if (ftStatus == FT_OK) {
    } else {
//        cout << __FUNCTION__ << " Device Reset Failed <0x" << hex;
//        cout << ftStatus << ">" << dec << endl;
        return -1;
    }
    Sleep(100);
    ftStatus = FT_SetBaudRate(ft_handle, FT_BAUD_19200);
    if (ftStatus == FT_OK) {
    } else {
//        cout << __FUNCTION__ << " Device Set Baudrate Failed <0x" << hex;
//        cout << ftStatus << ">" << dec << endl;
        return -1;
    }
    ftStatus = FT_SetDataCharacteristics(ft_handle, FT_BITS_8, FT_STOP_BITS_1, FT_PARITY_NONE);
    if (ftStatus == FT_OK) {
    } else {
//        cout << __FUNCTION__ << " Device Set Data characteristics Failed <0x" << hex;
//        cout << ftStatus << ">" << dec << endl;
        return -1;
    }
    ftStatus = FT_SetTimeouts(ft_handle, 1000, 1000);
    if (ftStatus == FT_OK) {
    } else {
//        cout << __FUNCTION__ << " Device Set Timeouts Failed <0x" << hex;
//        cout << ftStatus << ">" << dec << endl;
        return -1;
    }
    ftStatus = FT_SetFlowControl(ft_handle, FT_FLOW_RTS_CTS, 0, 0);
    if (ftStatus == FT_OK) {
    } else {
//        cout << __FUNCTION__ << " Device Set Flow control Failed <0x" << hex;
//        cout << ftStatus << ">" << dec << endl;
        return -1;
    }
    ULONG status;
    ftStatus = FT_GetModemStatus(ft_handle, &status);
    if (ftStatus == FT_OK) {
//        cout << __FUNCTION__ << " Modem status " << hex << status << endl;
    } else {
//        cout << __FUNCTION__ << " Device Get modem status Failed <0x" << hex;
//        cout << ftStatus << ">" << dec << endl;
        return -1;
    }
    // Set all pins to Output
    for (uint8_t idx = 0; idx < MAX_IO_PINS; idx ++) {
        pin_set(idx, OUTPUT);
    }
    found = true;
    return(device_mode_val_write());
}

int
FTDI_Device::pin_mode_get (uint8_t pin_num, uint8_t *mode)
{
    if (pin_num >= MAX_IO_PINS) {
//        cout << __FUNCTION__ << " Pins 0 - " << MAX_IO_PINS << " supported " << pin_num << endl;
        return -1;
    }
    uint8_t scratch_val;
    scratch_val = (pin_mode >> pin_num) & 0x1;
    *mode  = (PIN_INPUT == scratch_val) ? PIN_INPUT : PIN_OUTPUT;
	return 0;
}

int
FTDI_Device::device_mode_val_write ()
{
    uint8_t val_mode;
    val_mode = (write_val << 4) | pin_mode;
    return(FT_SetBitMode(ft_handle, val_mode, CBUS_BIG_BANG_MODE));
}

int
FTDI_Device::device_mode_val_read ()
{
    return(FT_GetBitMode(ft_handle, &read_val));
}

int
FTDI_Device::pin_set (uint8_t pin_num, io_type io)
{
    if (pin_num >= MAX_IO_PINS) {
//        cout << __FUNCTION__ << " Pins 0 - " << MAX_IO_PINS << " supported " << pin_num << endl;
        return -1;
    }
    set_bit_8(&pin_mode, pin_num, INPUT == io ? PIN_INPUT : PIN_OUTPUT);
    if (INPUT == io) return(device_mode_val_write());
    return 0;
}

int
FTDI_Device::pin_write (uint8_t pin_num, bool val)
{
    int err;
    if (pin_num >= MAX_IO_PINS) {
//        cout << __FUNCTION__ << " Pins 0 - " << MAX_IO_PINS << " supported " << pin_num << endl;
        return -1;
    }
    // Pin better be in output mode.
    uint8_t pin_mode;

    err = pin_mode_get(pin_num, &pin_mode);
    if (0 != err) return err; 
    if (PIN_OUTPUT == pin_mode) {
        set_bit_8(&write_val, pin_num, true == val ? 1 : 0);
    } else {
//        cout << __FUNCTION__ << " Pin " << pin_num << " is in Input Mode" << endl;
        return -1;
    }
    return(device_mode_val_write());
}

int
FTDI_Device::pin_read (uint8_t pin_num, bool *val)
{
    int err;
    if (pin_num >= MAX_IO_PINS) {
//        cout << __FUNCTION__ << " Pins 0 - " << MAX_IO_PINS << " supported " << pin_num << endl;
        return -1;
    }
    // Pin better be in output mode.
    uint8_t pin_mode;

    err = pin_mode_get(pin_num, &pin_mode);
    if (0 != err) return err; 
    if (PIN_INPUT == pin_mode) {
        err = device_mode_val_read();
        if (0 != err) return err; 
        *val = ((read_val >> pin_num)& 0x1) ? true: false;
    } else {
//        cout << __FUNCTION__ << " Pin " << pin_num << " is in Output Mode" << endl;
        return -1;
    }
    return 0;
}

bool
FTDI_Device::device_present (void)
{
    return found;
}
